package service

import (
	"context"
	"github.com/shopspring/decimal"
	"github.com/spf13/cast"
	"insighttracking/common/utils"
	"insighttracking/query/bo"
	"insighttracking/query/dao"
	"insighttracking/query/model"
	"strconv"
	"strings"
)

type _spuService struct{}

func (s _spuService) GetSpuByIdListApi(ctx context.Context, list []int64) ([]*model.SpuEntity, error) {
	return dao.SpuEntity.WithContext(ctx).Where(dao.SpuEntity.ID.In(list...)).Find()
}

func (s _spuService) GetSpuDetailByIdListApi(ctx context.Context, list []int64) (spuEntities []*bo.SpuBo, err error) {
	err = dao.SpuEntity.WithContext(ctx).Where(dao.SpuEntity.ID.In(list...)).Select(
		dao.SpuEntity.ALL,
		dao.UnitEntity.Name.As("UnitName"),
		dao.BrandEntity.Name.As("BrandName"),
		dao.SupplierEntity.Name.As("SupplierName"),
		dao.ProductionLineEntity.Name.As("ProductionLineName"),
	).
		LeftJoin(dao.UnitEntity, dao.UnitEntity.ID.EqCol(dao.SpuEntity.UnitID)).
		LeftJoin(dao.BrandEntity, dao.BrandEntity.ID.EqCol(dao.SpuEntity.BrandID)).
		LeftJoin(dao.SupplierEntity, dao.SupplierEntity.ID.EqCol(dao.SpuEntity.SupplierID)).
		LeftJoin(dao.ProductionLineEntity, dao.ProductionLineEntity.ID.EqCol(dao.SpuEntity.ProductionLineID)).
		Scan(&spuEntities)
	return
}

func (s _spuService) GetSpuPageApi(ctx context.Context, req bo.SpuPageBoReq) ([]bo.SpuBo, int64, error) {
	enterpriseId := cast.ToInt64(ctx.Value("enterpriseId"))
	offset, limit := utils.DbPage(req.PageNumber, req.PageSize)
	tx := dao.SpuEntity.WithContext(ctx).Where(
		dao.SpuEntity.EnterpriseID.Eq(enterpriseId),
	)
	if req.SpuType != "" {
		tx = tx.Where(dao.SpuEntity.SpuType.Eq(cast.ToInt64(req.SpuType)))
	}
	if req.Name != "" {
		req.Name = strings.ReplaceAll(req.Name, "%", "\\%")
		req.Name = strings.ReplaceAll(req.Name, "_", "\\%")
		tx = tx.Where(dao.SpuEntity.Name.Like("%" + req.Name + "%"))
	}
	if req.Status != "" {
		tx = tx.Where(dao.SpuEntity.Status.Eq(cast.ToInt64(req.Status)))
	}
	if req.CategoryId != "" {
		tx = tx.Where(dao.SpuEntity.CategoryID.Eq(cast.ToInt64(req.CategoryId)))
	}
	if req.IsWeighed != "" {
		tx = tx.Where(dao.SpuEntity.IsWeighed.Eq(cast.ToInt64(req.IsWeighed)))
	}
	var spuBo = make([]bo.SpuBo, 0)
	count, err := tx.Order(dao.SpuEntity.Sort, dao.SpuEntity.ID.Desc()).
		Select(
			dao.SpuEntity.ALL,
			dao.UnitEntity.Name.As("UnitName"),
			dao.BrandEntity.Name.As("BrandName"),
			dao.SupplierEntity.Name.As("SupplierName"),
			dao.ProductionLineEntity.Name.As("ProductionLineName"),
		).
		LeftJoin(dao.UnitEntity, dao.UnitEntity.ID.EqCol(dao.SpuEntity.UnitID)).
		LeftJoin(dao.BrandEntity, dao.BrandEntity.ID.EqCol(dao.SpuEntity.BrandID)).
		LeftJoin(dao.SupplierEntity, dao.SupplierEntity.ID.EqCol(dao.SpuEntity.SupplierID)).
		LeftJoin(dao.ProductionLineEntity, dao.ProductionLineEntity.ID.EqCol(dao.SpuEntity.ProductionLineID)).
		Order(dao.SpuEntity.Sort, dao.SpuEntity.ID.Desc()).
		ScanByPage(&spuBo, offset, limit)
	return spuBo, count, err

}

func (s _spuService) GetCategorySpuByCategoryIdApi(ctx context.Context, id int64) (*model.SpuEntity, error) {
	return dao.SpuEntity.WithContext(ctx).Where(dao.SpuEntity.CategoryID.Eq(id)).First()
}

func (s _spuService) CreateApi(ctx context.Context, m *model.SpuEntity) error {
	if err := dao.SpuEntity.WithContext(ctx).Create(m); err != nil {
		return err
	}
	return nil
}

func (s _spuService) GetBySpuIdApi(ctx context.Context, id int64) (*model.SpuEntity, error) {
	return dao.SpuEntity.WithContext(ctx).Where(dao.SpuEntity.ID.Eq(id)).First()
}

func (s _spuService) ModifySpuStatusAllApi(ctx context.Context, enterpriseId int64, status int64) error {
	if _, err := dao.SpuEntity.WithContext(ctx).Where(dao.SpuEntity.EnterpriseID.Eq(enterpriseId)).UpdateSimple(
		dao.SpuEntity.Status.Value(status),
	); err != nil {
		return err
	}
	return nil
}

func (s _spuService) GetBySupplierIdApi(ctx context.Context, id int64) (*model.SpuEntity, error) {
	return dao.SpuEntity.WithContext(ctx).Where(dao.SpuEntity.SupplierID.Eq(id)).First()
}

func (s _spuService) GetNameByIdApi(ctx context.Context, id int64) (name string, err error) {
	err = dao.SpuEntity.WithContext(ctx).Where(dao.SpuEntity.ID.Eq(id)).Pluck(dao.SpuEntity.Name, &name)
	return
}

func (s _spuService) GetSpuCategoryApi(ctx context.Context, enterpriseId int64) ([]*model.CategoryEntity, error) {
	return dao.CategoryEntity.WithContext(ctx).Where(
		dao.CategoryEntity.EnterpriseID.Eq(enterpriseId),
		dao.CategoryEntity.Status.Eq(cast.ToInt64("1")),
	).Find()
}

func (s _spuService) GetListByName(ctx context.Context, enterpriseId int64, name string, spuType int64) (resp []bo.AssociateResp, err error) {
	err = dao.SpuEntity.WithContext(ctx).
		Select(dao.SpuEntity.ALL, dao.UnitEntity.Name.As("UnitName")).
		LeftJoin(dao.UnitEntity, dao.UnitEntity.ID.EqCol(dao.SpuEntity.UnitID)).
		Where(
			dao.SpuEntity.Name.Like("%"+name+"%"),
			dao.SpuEntity.EnterpriseID.Eq(enterpriseId),
			dao.SpuEntity.SpuType.Eq(spuType),
			dao.SpuEntity.Status.Eq(1)).
		Order(dao.SpuEntity.Sort.Asc()).Scan(&resp)
	return
}

func (s _spuService) CreateBom(ctx context.Context, entity *model.SpuBomEntity) error {
	return dao.SpuBomEntity.WithContext(ctx).Create(entity)
}

func (s _spuService) BomList(ctx context.Context, req *bo.BomPageBoReq) (resp []*model.SpuBomEntity, count int64, err error) {
	tx := dao.SpuBomEntity.WithContext(ctx).Where(
		dao.SpuBomEntity.EnterpriseID.Eq(req.EnterpriseID))
	if req.Name != "" {
		req.Name = strings.ReplaceAll(req.Name, "%", "\\%")
		req.Name = strings.ReplaceAll(req.Name, "_", "\\%")
		tx = tx.Where(dao.SpuBomEntity.Name.Like("%" + req.Name + "%"))
	}
	if req.OrgSpuName != "" {
		req.OrgSpuName = strings.ReplaceAll(req.OrgSpuName, "%", "\\%")
		req.OrgSpuName = strings.ReplaceAll(req.OrgSpuName, "_", "\\%")
		tx = tx.Where(dao.SpuBomEntity.OrgSpuName.Like("%" + req.OrgSpuName + "%"))
	}
	if req.SpuName != "" {
		req.SpuName = strings.ReplaceAll(req.SpuName, "%", "\\%")
		req.SpuName = strings.ReplaceAll(req.SpuName, "_", "\\%")
		tx = tx.Where(dao.SpuBomEntity.SpuName.Like("%" + req.SpuName + "%"))
	}
	if req.ProductionLine != 0 {
		tx = tx.Where(dao.SpuBomEntity.ProductionLineIds.Like("%" + strconv.Itoa(req.ProductionLine) + "%"))
	}
	offset, limit := utils.DbPage(req.PageNumber, req.PageSize)
	return tx.FindByPage(offset, limit)
}

func (s _spuService) GetBomByCode(ctx context.Context, code string) (err error) {
	_, err = dao.SpuBomEntity.WithContext(ctx).Where(dao.SpuBomEntity.Code.Eq(code)).First()
	return err
}

func (s _spuService) GetOrgRequireBySpu(ctx context.Context, enterpriseID int64, spuMap map[int64]*bo.TotalOrderSpuBom) (resp map[int64]*bo.SpuRequireInfo, err error) {
	resp = make(map[int64]*bo.SpuRequireInfo)
	for spuID, item := range spuMap {
		var orgID int64
		_ = dao.SpuBomEntity.WithContext(ctx).Where(dao.SpuBomEntity.SpuID.Eq(spuID)).Pluck(dao.SpuBomEntity.OrgSpuID, &orgID)
		// 没有原材料，只需要计算自身的库存
		if orgID == 0 {
			orgID = spuID
			spuEntity, _ := SpuService.GetSpuDetailByIdApi(ctx, orgID)
			inventory := SpuInventoryService.GetAllSpuInventoryApi(ctx, enterpriseID, orgID)
			requireCount := item.Count.Sub(inventory)
			if requireCount.Compare(decimal.Zero) < 0 {
				requireCount = decimal.Zero
			}
			if _, ok := resp[orgID]; ok {
				resp[orgID].Count = resp[orgID].Count.Add(requireCount)
			} else {
				value := bo.SpuRequireInfo{
					SpuID:                orgID,
					Count:                requireCount,
					Entity:               spuEntity,
					OrdersSpuID:          item.OrderDetailID,
					ExpectationGoodsDate: item.ExpectationGoodsDate,
				}
				resp[orgID] = &value
			}
		} else {
			// 有原材料，拿到原材料库存，计算损耗率，再拿自身库存，反除损耗率
			orgSpuEntity, _ := SpuService.GetSpuDetailByIdApi(ctx, orgID)
			spuEntity, _ := SpuService.GetSpuDetailByIdApi(ctx, spuID)
			orgInventory := SpuInventoryService.GetAllSpuInventoryApi(ctx, enterpriseID, orgID)
			inventory := SpuInventoryService.GetAllSpuInventoryApi(ctx, enterpriseID, spuID)
			requireCount := item.Count.Sub(inventory).Sub(orgInventory.Mul(spuEntity.LossRate).Div(decimal.New(100, 0))).Div(decimal.New(100, 0).Sub(spuEntity.LossRate).Div(decimal.New(100, 0)))
			if _, ok := resp[orgID]; ok {
				resp[orgID].Count = resp[orgID].Count.Add(requireCount)
			} else {
				value := bo.SpuRequireInfo{
					SpuID:                orgID,
					Count:                requireCount,
					Entity:               orgSpuEntity,
					ExpectationGoodsDate: item.ExpectationGoodsDate,
				}
				resp[orgID] = &value
			}
		}
	}
	return
}

func (s _spuService) GetSpuDetailByIdApi(ctx context.Context, id int64) (spuEntities *bo.SpuBo, err error) {
	err = dao.SpuEntity.WithContext(ctx).Where(dao.SpuEntity.ID.Eq(id)).Select(
		dao.SpuEntity.ALL,
		dao.UnitEntity.Name.As("UnitName"),
		dao.BrandEntity.Name.As("BrandName"),
		dao.SupplierEntity.Name.As("SupplierName"),
		dao.CategoryEntity.Name.As("CategoryName"),
	).
		LeftJoin(dao.UnitEntity, dao.UnitEntity.ID.EqCol(dao.SpuEntity.UnitID)).
		LeftJoin(dao.SupplierEntity, dao.SupplierEntity.ID.EqCol(dao.SpuEntity.SupplierID)).
		LeftJoin(dao.CategoryEntity, dao.CategoryEntity.ID.EqCol(dao.SpuEntity.CategoryID)).
		LeftJoin(dao.BrandEntity, dao.BrandEntity.ID.EqCol(dao.SpuEntity.BrandID)).
		Scan(&spuEntities)
	return
}

func (s _spuService) GetUnitNameBySpuIdApi(ctx context.Context, id int64) (name string, err error) {
	err = dao.SpuEntity.WithContext(ctx).
		Select(dao.UnitEntity.Name.As("UnitName")).
		LeftJoin(dao.UnitEntity, dao.UnitEntity.ID.EqCol(dao.SpuEntity.UnitID)).
		Where(dao.SpuEntity.ID.Eq(id)).Pluck(dao.UnitEntity.Name, &name)
	return
}

var SpuService = new(_spuService)
